前面2-11用thread舉例有提到過共享記憶體可能會發生Race Condition,而Process可以分成兩大類
Race Condition 競爭情況
相同的運作. 因執行順序不同, 造成處理的結果不同。舉例:
以下情境有兩個thread同時在對同一個變數進行存取,我故意在中間加一些sleep模擬可能因為各種因素影響執行花費時間
fun main() {
// 此變數是共享的記憶體
var n = 0
// 原本預期t1執行結果 n 是1, t2執行結果 n 是2,但結果兩個都是1,原因是t1和t2在存取變數n的時間並不是由上而下先走完t1在走t2,為了不讓cpu閒置,可能因為t1在等待過程中就先讓t2執行
// thread 1
thread {
sleep(200)
val temp = n
println("thread 1 run")
sleep(800)
n = temp+1//
println("thread 1 n $n")
}
// thread 2
thread {
val temp = n
println("thread 2 run")
sleep(500)
n = temp+1//
println("thread 2 n $n")
}
}
執行結果
thread 2 run
thread 1 run
thread 2 sum 1
thread 1 sum 1
再舉一個例子,以下t1和t2各跑100圈,每一次都對n進行加一,由於t1和t2過程中取n時可能會取到相同的值(像上述狀況),各自加1後存回給n,因此會造成結果可能不是200的情況
fun main() {
var n = 0
val t1 = thread {
println("t1 start")
(1..100).forEach {// 跑100圈
n++
}
println("t1 end")
}
val t2 = thread {
println("t2 start")
(1..100).forEach {// 跑100圈
n++
}
println("t2 end")
}
t1.join()// 等待t1執行完畢
t2.join()// 等待t2執行完畢
println("n $n")// 預期應該是200,實際上每次print出來結果會不同
}
執行結果
t1 start
t2 start
t1 end
t2 end
n 110
臨界區間 Critical Section
要解決RaceConditon就可以使用臨界區間(簡稱C.S)的概念,他有三大特性
概念其實就是很像設計了一間廁所(C.S),對應三大特性
以下介紹針對兩個process(會用i和j標示)和多個procress進行臨界區間設計的演算法
2個procress_演算法_1
第一種演算法很像在廁所加了一道鎖,turn就像一把鑰匙,有鑰匙的才能開門進去,可能會發生鑰匙在我手上但我不想上廁所讓別人乾等的情況(所謂占著茅坑不拉屎最佳典範)
分析:
滿足 mutual exclusion (互斥)
說明:
當 Pi, Pj 皆欲進入 C.S., 又 turn 不會同時為 i, j (因為 i 不等於 j), 故只能一個 process 進入 C.S.違反 progress (可進行性)
說明:
當 Pj 於 R.S. 中 (因為 j 就是不想上廁所), 但 turn = j 鑰匙在j手上, 此時若 Pi 想進入 C.S., 將被卡在 while loop 中無法進入 (因為違反 progress)
2個procress_演算法_2
第二種像是每個人都有一個立牌(flag)可以舉,想上廁所的就舉牌,但你要進去之前要先看另一個人有沒有舉牌,有的話就要等他,所以就會發生兩個人同時都舉著牌子在等對方進去僵持不下
分析:
滿足 mutual exclusion (互斥)
說明:
若欲使 Pi, Pj 同時進入 C.S., 則 flag[i] = flag[j] = false, 但一開始 flag會改為 true, 因此上述情況不可能存在不滿足 progress (可進行性)
說明:
當 Pi, Pj 皆欲進入 C.S.,則 flag[i] = flag[j] = true,此時 2 者皆會卡在 while loop 中 => deadlock,故違反 progress
上一個範例是拿到鑰匙那個人不想進廁所,這次是兩個都想上廁所的卡在門外等對方先進去,做人好難...
2個procress_演算法_3
(結合了1和2)
同時可以有鑰匙(turn)和知道誰想進c.s(flag),這個我們要讓i和j執行過程表示出來會比較清楚:
時間順序 | i | j |
---|---|---|
1 | flag[i]= true | - |
2 | - | flag[j]= true |
3 | turn=j(把鑰匙給對方) | - |
4 | - | turn=i(把鑰匙給對方) |
5 | 如果對方想進C.S(flag[j]= true)而且鑰匙在他手上turn=j我就不做事(走到第5步的時候因為鑰匙已經回到i手上所以i會進C.S) | - |
6 | i出C.S,flag[i]= flase,進入R.S | - |
7 | - | while(flag[i]= true & turn==i)do no-op不成立(因為flag[i]= flase),所以換j進C.S |
分析:
滿足 mutual exclusion (互斥)
說明:
當 2 process 皆欲進入 C.S., 則 flag[i] = flag[j] = true, 但 turn 不會同時為 i, j (因為 i 不等於 j), 故只有一 process 可進入 C.S.滿足 progress (可進行性)
說明:
當 Pj 不想進入 C.S., 又 turn = j, 且此時 flag[j] = false, 故當 Pi 想進入, 可以順利通過 while loop
當 2 process 皆欲進入 C.S., 此時視 turn 的值為 i 或 j, 指向者可進入 C.S., 所以 No deadlock滿足 Bounded Waiting (有限性等待)
說明:
當 Pi, Pj 皆欲進入 C.S., 而 turn = j時, 則 Pj 可進入, 若 Pj 離開後立即再度欲進入 C.S., 則:
flag[j] = true;
turn = i
因為 turn = i, 故下次必由 Pj 進入 C.S. 中
明天再看多個procress進行臨界區間設計的演算法好了。
分類會依照第一篇介紹的分類架構來進行
由於是將學習過程記錄下來,如果有任何錯誤歡迎糾正
以下參考連結在學習過程中覺得非常有幫助:
-Chapter3-作業系統-程序間的溝通
-[OS] 作業系統筆記-Process間的溝通